# This file is intended to be a final submission. python tester.py Player.py
# should work at all times. If it does not, there is a bug.
# If you're just trying to test a solution, scroll down to the Player
# class.

# This file is intended to be in the same format as a valid solution, so
# that users can edit their solution into Player and then submit just this
# file to the contest. If you see any reason this would not work, please submit
# an Issue to https://github.com/ChadAMiller/hungergames/issues or email me.

# You can see more sample player classes in bots.py

import math
import random

class BasePlayer(object):
    '''
    Base class so I don't have to repeat bookkeeping stuff.
    Do not edit unless you're working on the simulation.
    '''
    
    def __str__(self):
        try:
            return self.name
        except AttributeError:
            # Fall back on Python default
            return super(BasePlayer, self).__repr__()
    
    def hunt_choices(*args, **kwargs):
        raise NotImplementedError("You must define a strategy!")
        
    def hunt_outcomes(*args, **kwargs):
        pass
        
    def round_end(*args, **kwargs):
        pass


class Player(BasePlayer):
    #A modified version of tit-for-tat where you slack with the lowest few reputation people and rip off the high reputation people a bit, with some extras.
    #Relatively high variance as compared to some of our other strategies but it seems to work.
    
    def __init__(self):
        self.name = "IAMACOW"
        self.to_hunt=0
        self.myRep=0
        self.currentRep=[]
        self.toplim=0
        self.huntprob=0
        self.food_tracking=0
        self.currentFood=0
        self.performance=0
        self.fix=False

    def hunt_choices(self, round_number, current_food, current_reputation, m, player_reputations):
        
        if round_number==1: self.food_tracking=300*len(player_reputations)*(len(player_reputations)+1)
        self.currentRep=player_reputations
        self.currentFood=current_food
        self.myRep=current_reputation
        
        repdict=dict(zip(xrange(len(player_reputations)), player_reputations))
        reps_to_hunt=[]
        decisions=[]

        if len(player_reputations)==1: return ['s'] #Always slack with only two people remaining. Optimal strategy, since anything else could lose more. We are simply trying to starve the opponent by attrition.

        for rep in sorted(repdict, key=repdict.get, reverse=True):
            if repdict[rep]>=self.toplim: #Rip off high rep people a bit for some profits, but not too much.
                pass
            elif ((self.to_hunt>0) and (random.random()<=self.huntprob)):
                reps_to_hunt.append(rep) #Hunt with moderately high rep people, equal to the number of people who hunted with you last round. This is equivalent to not hunting with low rep people. A form of tit for tat.
                self.to_hunt-=1
        
        for a in xrange(len(player_reputations)):
            if round_number==1:
                decisions=['h' for i in xrange(len(player_reputations))]
                break
            if a in reps_to_hunt:
                decisions.append('h')
            else:
                decisions.append('s') 
        
        return decisions

    def hunt_outcomes(self, food_earnings):
        
        self.to_hunt=0
        for a in food_earnings:
            if ((a==0) or (a==1)):
                self.to_hunt+=1 #Counts the number of times you have been hunted with.
                
        return 0

    def round_end(self, award, m, number_hunters):
        
        p=len(self.currentRep)
        self.food_tracking+= (p*(p+1) + 2*number_hunters) if number_hunters>=m else (-p*(p+1) + 2*number_hunters) #-2 net food per person choosing to slack is an invariant.
        avg_food=(self.food_tracking-self.currentFood)/p
        avg_rep=sum(self.currentRep)/p
        std_dev=(self.myRep-avg_rep)**2
        for i in self.currentRep: std_dev+=(i-avg_rep)**2
        std_dev=math.sqrt(std_dev/(p+1)) #standard reputation deviation
        self.performance=(self.currentFood-avg_food)/avg_food #performance measure, benchmark against everyone else.

        if self.performance>=0 or self.myRep<=avg_rep+0.3*std_dev: #Reputation check.
            self.fix = False
            self.toplim=0.78+math.atan(self.performance)/(10*math.pi) #Variance of +0.05 based on performance. Cooperate when doing well to get some extra rep (but not too much)...
            self.huntprob=0.9
        elif not self.fix:
            self.toplim=0.69+math.atan(self.performance)/(10*math.pi) #And rip off when not doing so well. arctan is to "normalize" the performance.
            self.huntprob=0.87+math.atan(self.performance)/(10*math.pi) #The constants are a bit arbitrary.
            self.fix = True #This is to fix the ripoff rate when things are bad, so as to accelerate the recovery to +ve performance, since as performance approaches 0 (from below) the recover slows)
            
        return 0
